/*
 * Copyright (C) 2013 Sony Mobile Communications AB
 *
 * This file is part of ChkBugReport.
 *
 * ChkBugReport is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * ChkBugReport is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ChkBugReport.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.sonyericsson.chkbugreport.plugins.logs.kernel.iptables;

import com.sonyericsson.chkbugreport.BugReportModule;
import com.sonyericsson.chkbugreport.doc.Chapter;
import com.sonyericsson.chkbugreport.plugins.logs.LogLine;
import com.sonyericsson.chkbugreport.plugins.logs.LogLines;
import com.sonyericsson.chkbugreport.plugins.logs.kernel.KernelLogData;
import com.sonyericsson.chkbugreport.util.Util;

import java.util.Vector;
import java.util.regex.Pattern;

/**
 * Parses the logs generated by iptables to create statistics
 * e.g. log:
 *   "OUT IN= OUT=rmnet_usb0 SRC=10.143.3.219 DST=81.0.212.201 LEN=40 TOS=0x00 PREC=0x00 TTL=64 ID=0 DF PROTO=TCP SPT=57017 DPT=80 SEQ=3467384767 ACK=683353950 WINDOW=229 RES=0x00 ACK URGP=0"
 *
 */
public class IPTableLogAnalyzer {

    private KernelLogData mLogData;
    private BugReportModule mMod;
    private LogLines mLogs;
    private Vector<Packet> mPackets = new Vector<Packet>();

    public IPTableLogAnalyzer(KernelLogData logData, BugReportModule mod, LogLines logs) {
        mLogData = logData;
        mMod = mod;
        mLogs = logs;
    }

    public Vector<Packet> getPackets() {
        return mPackets;
    }

    public Chapter createChapter(String title) {
        Chapter ch = new Chapter(mMod.getContext(), title);
        mLogData.addChapter(ch);
        return ch;
    }

    public void run() {
        // Parse all packets
        Pattern p = Pattern.compile(".*IN=.* OUT=.* SRC=.* DST=.* LEN=.*");
        Packet lastPkt = null;
        for (LogLine ll : mLogs) {
            if (p.matcher(ll.msg).matches()) {
                Packet pkt = parse(ll);
                if (pkt.ok) {
                    mPackets.add(pkt);
                    // NOTE: sometimes the same packet is logged twice, so lets keep only the last one
                    if (pkt.isSame(lastPkt)) {
                        mPackets.remove(lastPkt);
                    }
                    lastPkt = pkt;
                }
            }
        }

        if (mPackets.isEmpty()) {
            return;
        }

        // Analyze packets
        new SimpleStats(this).run(mMod);
        new ResendStats(this).run(mMod);
        new ConnectionGrouping(this).run();
    }

    private Packet parse(LogLine ll) {
        Packet pkt = parseAttrs(ll.msg);
        pkt.ts = ll.ts;
        pkt.realTs = ll.realTs;
        pkt.log = new LogLine(ll); // create a deep copy so we can modify it
        if ("TCP".equals(pkt.proto)) {
            pkt.log.css = "packet-tcp";
        } else if ("UDP".equals(pkt.proto)) {
            pkt.log.css = "packet-udp";
        } else if ("ICMP".equals(pkt.proto)) {
            pkt.log.css = "packet-icmp";
        } else {
            pkt.log.css = "packet-unknown";
        }
        return pkt;
    }

    private Packet parseAttrs(String log) {
        Packet pkt = new Packet();
        log = Util.strip(log);

        // Extract prefix
        int idx = log.indexOf("IN=");
        if (idx > 0) {
            pkt.prefix = log.substring(0, idx);
            log = log.substring(idx);
        }
        pkt.hash = log.hashCode();

        // Extract referenced packet
        idx = log.indexOf('[');
        if (idx > 0) {
            int idx2 = log.lastIndexOf(']');
            if (idx2 > idx) {
                pkt.ref = parseAttrs(log.substring(idx + 1, idx2 - 1));
            }
            log = log.substring(0, idx - 1);
        }

        // Split log line and extract attributes and flags
        String fields[] = log.split(" ");
        for (String f : fields) {
            if (f.contains("=")) {
                String kv[] = f.split("=", 2);
                pkt.put(kv[0], kv[1]);
            } else {
                pkt.addFlag(f);
            }
        }

        // Check parsed packet
        pkt.check();
        return pkt;
    }

}
